package pacman;

import pacman.objects.*;
import java.awt.event.KeyEvent;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.EmptyStackException;
import java.util.Stack;

/**
 * This class is the main class of the project, it do all the game events handling
 * @author Fernando Pasquini
 */
public class Engine
{
  private boolean paused;
  private InputOutput inputoutput;
  private Maps levels;
  private Characters characters;
  private Dijkstra ia;

  /**
   * Starts a window of the level1 of the game
   * @throws FileNotFoundException
   * @throws IOException
   * @throws InterruptedException
   */
  public Engine() throws FileNotFoundException, IOException, InterruptedException {
      this.levels = new Maps(21, 20);
      this.inputoutput = new InputOutput(856, 838);
      this.characters = new Characters();

      this.ia = new Dijkstra(levels,  levels.getSizeX(), levels.getSizeY(), 40);

      this.levels.loadMap("Nivel1.map");
      this.characters.loadCharacters("Nivel1.char");
      this.paused = true;

      this.inputoutput.repaint();
      Thread.sleep(10);
  }

  /**
   * Do the keyboard event handling
   * Get the W,A,S,D keys to move pacman and SPACE to pause / unpause the game
   */
  public void readKeyboard()
  {
    if(inputoutput.getKeyboardBuffer(KeyEvent.VK_SPACE))
    {
      paused = !paused;
      inputoutput.setKeyboardBuffer(KeyEvent.VK_SPACE, false);
    }

    if (paused == false)
    {
      if(inputoutput.getKeyboardBuffer(KeyEvent.VK_A))
      {
        characters.getPacman().setHeadingAngle((float)Math.PI);
        return;
      }

      if(inputoutput.getKeyboardBuffer(KeyEvent.VK_W))
      {
        characters.getPacman().setHeadingAngle((float)Math.PI / 2);
        return;
      }

      if(inputoutput.getKeyboardBuffer(KeyEvent.VK_D))
      {
        characters.getPacman().setHeadingAngle(0);
        return;
      }

      if(inputoutput.getKeyboardBuffer(KeyEvent.VK_S))
      {
        characters.getPacman().setHeadingAngle(3 * (float)Math.PI / 2);
        return;
      }
    }
  }

  /**
   * Do the clicked mouse button event handling
   * Seek if there's a ghost collided with the pacman and atack that ghost
   */
  public void readMouseButton()
  {
    float heding = characters.getPacman().getHeadingAngle();
          Ghost currentGhost;
          int i = 0;
    if(inputoutput.getMouseButton(0))
    {
      switch((int)(180*heding/Math.PI))
      {
        case 0:
          while ((currentGhost = characters.getGhost(i)) != null){
            if(characters.getPacman().runGhostHitTest(currentGhost) && currentGhost.getPosX() > characters.getPacman().getPosX())
            {
              characters.getPacman().basicAttack(characters.getGhost(i));
              if(characters.getGhost(i).isDead() == true)
                characters.getPacman().increaseExperience(100);
            }
            i++;
          }
        break;

        case 90:
          while ((currentGhost = characters.getGhost(i)) != null){
            if(characters.getPacman().runGhostHitTest(currentGhost) && currentGhost.getPosY() < characters.getPacman().getPosY())
            {
              characters.getPacman().basicAttack(characters.getGhost(i));
              if(characters.getGhost(i).isDead() == true)
                characters.getPacman().increaseExperience(100);
            }
            i++;
          }
        break;

        case 180:
          while ((currentGhost = characters.getGhost(i)) != null){
            if(characters.getPacman().runGhostHitTest(currentGhost) && currentGhost.getPosX() < characters.getPacman().getPosX())
            {
              characters.getPacman().basicAttack(characters.getGhost(i));
              if(characters.getGhost(i).isDead() == true)
                characters.getPacman().increaseExperience(100);
            }
            i++;
          }
        break;

        case 270:
          while ((currentGhost = characters.getGhost(i)) != null){
            if(characters.getPacman().runGhostHitTest(currentGhost) && currentGhost.getPosY() > characters.getPacman().getPosY())
            {
              characters.getPacman().basicAttack(characters.getGhost(i));
              if(characters.getGhost(i).isDead() == true)
                characters.getPacman().increaseExperience(100);
            }
            i++;
          }
        break;
      }
    }

    if(inputoutput.getMouseButton(2))
    {
      switch((int)(180*heding/Math.PI))
      {
        case 0:
          while ((currentGhost = characters.getGhost(i)) != null){
            if(characters.getPacman().runGhostHitTest(currentGhost) && currentGhost.getPosX() > characters.getPacman().getPosX())
            {
              characters.getPacman().Attack(characters.getGhost(i));
              if(characters.getGhost(i).isDead() == true)
                characters.getPacman().increaseExperience(100);
            }
            i++;
          }
        break;

        case 90:
          while ((currentGhost = characters.getGhost(i)) != null){
            if(characters.getPacman().runGhostHitTest(currentGhost) && currentGhost.getPosY() < characters.getPacman().getPosY())
              characters.getPacman().Attack(characters.getGhost(i));
            i++;
          }
        break;

        case 180:
          while ((currentGhost = characters.getGhost(i)) != null){
            if(characters.getPacman().runGhostHitTest(currentGhost) && currentGhost.getPosX() < characters.getPacman().getPosX())
              characters.getPacman().Attack(characters.getGhost(i));
            i++;
          }
        break;

        case 270:
          while ((currentGhost = characters.getGhost(i)) != null){
            if(characters.getPacman().runGhostHitTest(currentGhost) && currentGhost.getPosY() > characters.getPacman().getPosY())
              characters.getPacman().Attack(characters.getGhost(i));
            i++;
          }
        break;
      }
    }

  }

  /**
   * Do the scroll mouse wheel event handling
   * Change the active skill
   */
  public void readMouseWheel(){
      int aux = inputoutput.getMouseWheel();
      if(aux == -1){
          characters.getPacman().nextSkill();
          System.out.println(characters.getPacman().activeSkill);
         }
      if(aux == 1){
          characters.getPacman().prevSkill();
         }
  }
  
  /**
   * Main loop of the game where all character computaion occurs
   */
  public void runGame() {
    Stack<Ghost> quadrant1_ghosts = new Stack<Ghost>();
    Stack<Ghost> quadrant2_ghosts = new Stack<Ghost>();
    Stack<Ghost> quadrant3_ghosts = new Stack<Ghost>();
    Stack<Ghost> quadrant4_ghosts = new Stack<Ghost>();

    readKeyboard();
    readMouseButton();
    readMouseWheel();

    if (paused == false)
    {
      Ghost currentGhost;
      int i = 0;

      //Pacman
      if (characters.getPacman().runMapHitTest(levels.getMap()) == false)
      {
        characters.getPacman().updatePosition(levels.getSizeX(), levels.getSizeY());
        if(characters.getPacman().eat(levels.getMap()[(int)characters.getPacman().getPosX() / 40][(int)characters.getPacman().getPosY() / 40]))
          levels.setMapContent(new Floor((int)characters.getPacman().getPosX() / 40, (int)characters.getPacman().getPosY() / 40));
      }

      //Ghosts
      while ((currentGhost = characters.getGhost(i)) != null)
      {
        if (currentGhost.isDead() == false)
        {
          if(currentGhost.getPosX() > characters.getPacman().getPosX() && currentGhost.getPosY() > characters.getPacman().getPosY())
            quadrant1_ghosts.push(currentGhost);
          if(currentGhost.getPosX() <= characters.getPacman().getPosX() && currentGhost.getPosY() > characters.getPacman().getPosY())
            quadrant2_ghosts.push(currentGhost);
          if(currentGhost.getPosX() <= characters.getPacman().getPosX() && currentGhost.getPosY() <= characters.getPacman().getPosY())
            quadrant3_ghosts.push(currentGhost);
          if(currentGhost.getPosX() > characters.getPacman().getPosX() && currentGhost.getPosY() <= characters.getPacman().getPosY())
            quadrant4_ghosts.push(currentGhost);
        }
        i++;
      }

      while(true)
      {
        boolean endOfGhosts = true;

        ia.createCostMatrix((int)characters.getPacman().getPosX(), (int)characters.getPacman().getPosY());
        try {
          currentGhost = quadrant1_ghosts.pop();
          if (currentGhost.runIAattack(ia, levels.getSizeX(), levels.getSizeY()) && !characters.getPacman().runGhostHitTest(currentGhost))
            currentGhost.updatePosition(levels.getSizeX(), levels.getSizeY());
          ia.updateWeight((int)currentGhost.getPosX()/40, (int)currentGhost.getPosY()/40);
          endOfGhosts = false;
        } catch (EmptyStackException e){}

        ia.createCostMatrix((int)characters.getPacman().getPosX(), (int)characters.getPacman().getPosY());
        try {
          currentGhost = quadrant2_ghosts.pop();
          if (currentGhost.runIAattack(ia, levels.getSizeX(), levels.getSizeY()) && !characters.getPacman().runGhostHitTest(currentGhost))
            currentGhost.updatePosition(levels.getSizeX(), levels.getSizeY());
          ia.updateWeight((int)currentGhost.getPosX()/40, (int)currentGhost.getPosY()/40);
          endOfGhosts = false;
        } catch (EmptyStackException e){}

        ia.createCostMatrix((int)characters.getPacman().getPosX(), (int)characters.getPacman().getPosY());
        try {
          currentGhost = quadrant3_ghosts.pop();
          if (currentGhost.runIAattack(ia, levels.getSizeX(), levels.getSizeY()) && !characters.getPacman().runGhostHitTest(currentGhost))
            currentGhost.updatePosition(levels.getSizeX(), levels.getSizeY());
          ia.updateWeight((int)currentGhost.getPosX()/40, (int)currentGhost.getPosY()/40);
          endOfGhosts = false;
        } catch (EmptyStackException e){}

        ia.createCostMatrix((int)characters.getPacman().getPosX(), (int)characters.getPacman().getPosY());
        try {
          currentGhost = quadrant4_ghosts.pop();
          if (currentGhost.runIAattack(ia, levels.getSizeX(), levels.getSizeY()) && !characters.getPacman().runGhostHitTest(currentGhost))
            currentGhost.updatePosition(levels.getSizeX(), levels.getSizeY());
          ia.updateWeight((int)currentGhost.getPosX()/40, (int)currentGhost.getPosY()/40);
          endOfGhosts = false;
        } catch (EmptyStackException e){}

        if(endOfGhosts == true)
          break;
      }
    }
    ia.eraseWeightMatrix();
    inputoutput.loadRenderBuffer(levels, characters);
    inputoutput.repaint();
  }

  /**
   *
   */
  public void isAllGhostsDead()
  {
    int i = 0;
    Ghost currentGhost;
    while ((currentGhost = characters.getGhost(i)) != null)
    {
      if (currentGhost.isDead() == false)
      {
        return;
      }
      i++;
    }

    i = 0;
    while ((currentGhost = characters.getGhost(i)) != null)
    {
      currentGhost.levelUp();
      currentGhost.ressurect();
      i++;
    }
  }
}